package com.vn.ld.btc.dao.impl;

import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.vn.ld.btc.dao.IBaseDao;
import com.vn.ld.btc.dao.common.SearchQueryTemplate;
import com.vn.ld.btc.dao.common.SearchResult;
import com.vn.ld.btc.dao.util.QueryBuilderUtil;

@Transactional
public class BaseDao<E> implements IBaseDao<E> {
	@Autowired
	private EntityManager entityManager;

	public E findByID(Class<E> entity, Serializable id) {
		return this.entityManager.find(entity, id);
	}

	public void save(E instance) {
		this.entityManager.persist(instance);
	}

	public void delete(E instance) {
		this.entityManager.remove(instance);
	}

	public E merge(E instance) {
		return this.entityManager.merge(instance);
	}

	@SuppressWarnings("unchecked")
	public List<E> findByProperty(String propertyName, Object value) {
		StringBuffer strQuery = new StringBuffer();
		strQuery.append(" SELECT e FROM ")
				.append(getGenericClass())
				.append(" as e ")
				.append("WHERE ")
				.append("e.activeFlg = 1 and ")
				.append("e.")
				.append(propertyName)
				.append(" := ")
				.append(propertyName);
		Query query = this.entityManager.createQuery(strQuery.toString());
		query.setParameter(propertyName, value);
		List<E> result = query.getResultList();
		return result;
	}

	@SuppressWarnings("unchecked")
	public List<E> findByProperty(String propertyName, String orderName, Object value, boolean desc) {
		StringBuffer strQuery = new StringBuffer();
		strQuery.append(" SELECT e FROM ")
				.append(getGenericClass())
				.append(" as e ")
				.append("WHERE ")
				.append("e.activeFlg = 1 and ")
				.append("e.")
				.append(propertyName)
				.append(" := ")
				.append(propertyName)
				.append(" order by e.")
				.append(orderName);
		if (desc) {
			strQuery.append(" DESC ");
		} else {
			strQuery.append(" ASC ");
		}

		Query query = this.entityManager.createQuery(strQuery.toString());
		query.setParameter(propertyName, value);
		List<E> result = query.getResultList();
		return result;
	}

	@SuppressWarnings("unchecked")
	public List<E> findAll() {
		StringBuffer strQuery = new StringBuffer();
		strQuery.append(" SELECT e FROM ")
				.append(getGenericClass())
				.append(" as e ")
				.append("WHERE ")
				.append("e.activeFlg = 1");
		Query query = this.entityManager.createQuery(strQuery.toString());
		List<E> results = query.getResultList();
		return results;
	}

	/**
	 * Find data with query template
	 * 
	 * @param queryTemplate
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> SearchResult<T> find(SearchQueryTemplate queryTemplate) {
		try {
			SearchResult<T> searchResults = new SearchResult<T>();
			// Get list data
			Query queryData = queryTemplate.getQuery(this.entityManager);
			// Add Pagging info
			QueryBuilderUtil.addPagging(queryData, queryTemplate.getPageObjInfo());
			// Get list data
			List<T> resultData = queryData.getResultList();

			if (resultData != null) {
				searchResults.addAll(resultData);
			}

			// Count data
			Query queryCount = queryTemplate.getCountQuery(this.entityManager);
			Object countObj = queryCount.getSingleResult();

			QueryBuilderUtil.setTotalValue(queryTemplate.getPageObjInfo(), countObj);

			searchResults.setPageObjInfo(queryTemplate.getPageObjInfo());

			return searchResults;
		} catch (RuntimeException e) {
			throw e;
		}
	}

	private String getGenericClass() {
		Type c = getClass().getGenericSuperclass();
		String s = c.toString();
		Pattern MY_PATTERN = Pattern.compile("\\<(.*?)\\>");
		Matcher m = MY_PATTERN.matcher(s);
		String generic;
		if (m.find()) {
			generic = m.group(1);
		} else
			generic = "null";

		return generic;
	}

	public EntityManager getEntityManager() {
		return this.entityManager;
	}
}
